/*
 ============================================================================
 Name		: RCallStack.cpp
 Author	  : wangli@opi-corp.com
 Version	 : 1.0
 Copyright   : Your copyright notice
 Description : RCallStack implementation
 ============================================================================
 */

#include "RCallStack.h"
#include "TItem.h"
#include "TStringItem.h"

enum EStackPanicCode
{
    KPanicSize = 1,
    KPanicPermision,
    KPanicOverflow,
    KPanicEmpty, 
    KPanicDumpDefault
};
_LIT(KStackCategory, "RCallStack");
static void StackPanic(EStackPanicCode aErrorCode)
{
    User::Panic(KStackCategory, aErrorCode);
}

RCallStack::RCallStack()
    :iChunkTop(NULL), iChunkBottom(NULL)
{
}

RCallStack::~RCallStack()
{
    Close();
}

void RCallStack::Close()
{
    iStackChunk.Close();
    iChunkBottom = NULL;
    iChunkTop = NULL;
}

void RCallStack::CreateL(const TDesC& aStackName, TInt aSize)
{
    __ASSERT_DEBUG(aStackName.Length() > 0 && 
                   aStackName.Length() < KMaxFullName,
                   User::Invariant());
    __ASSERT_DEBUG(aSize > 0, User::Invariant());
    
    User::LeaveIfError(iStackChunk.CreateGlobal(aStackName, aSize, aSize));
    
    iChunkBottom = iStackChunk.Base();
    __ASSERT_ALWAYS(aSize == iStackChunk.Size(), StackPanic(KPanicSize));
    iChunkTop = iChunkBottom + iStackChunk.Size();
    
    memset(iChunkBottom, 0, aSize);
}

void RCallStack::OpenL(const TDesC& aStackName)
{
    __ASSERT_DEBUG(aStackName.Length() > 0 &&
                   aStackName.Length() < KMaxFullName,
                   User::Invariant());
    
    iStackChunk.Close();
    User::LeaveIfError(iStackChunk.OpenGlobal(aStackName, EFalse));
    __ASSERT_ALWAYS(iStackChunk.IsWritable(), StackPanic(KPanicPermision));
    iChunkBottom = iStackChunk.Base();
    iChunkTop = iChunkBottom + iStackChunk.Size();
}

void RCallStack::checkFreeSize(TUint aSize)const
{
    TUint size = *getSp();
    TUint freeSize = iChunkTop - iChunkBottom - 
                     2*sizeof(TUint) - size;
    if(aSize > freeSize)
        StackPanic(KPanicOverflow);
}

void RCallStack::Push(const TItem& aItem)
{
    __ASSERT_DEBUG(&aItem != NULL && aItem.Size() > 0, User::Invariant());
    
    checkFreeSize(AlignSize(aItem.Size()));
    
    TUint* sp = getSp();
    TUint* pCount = getCount();
    TUint8* top = getTop() - AlignSize(aItem.Size());
   // __ASSERT_ALWAYS(top >= iChunkBottom + 2*sizeof(TUint), User::Invariant());
    aItem.Copy(top);
    *sp += AlignSize(aItem.Size());
    *pCount += 1;
    __ASSERT_ALWAYS(top == iChunkTop - (*sp), User::Invariant());
}

void RCallStack::Push(EItemType aType, TUint16 aSize, const TAny* aObject)
{
    __ASSERT_DEBUG(aSize > 0 && aObject != NULL, User::Invariant());
    
   TUint objectSize = AlignSize(aSize) + sizeof(TItem);
   checkFreeSize(AlignSize(objectSize));
   
   TUint8* top = getTop() - AlignSize(objectSize);
   TItem* pCopy = TItem::ConstructItem(top, aType, aSize, aObject);
   __ASSERT_ALWAYS((TAny*)top == (TAny*)pCopy, User::Invariant());
   TUint* sp =getSp();
   TUint* pCount = getCount();
   *sp += AlignSize(objectSize);
   *pCount += 1;
   __ASSERT_ALWAYS(top == iChunkTop - (*sp), User::Invariant());
}

void RCallStack::Pop()
{
    if(isEmpty())
        return ;
    
    TItem* pItem = (TItem*)getTop();
    TUint size = AlignSize(pItem->Size());
    TUint* sp = getSp();
    TUint* pCount = getCount();
    *sp = *sp - size;
    *pCount -= 1;
}

const TItem& RCallStack::Top()const
{
    if(isEmpty())
        StackPanic(KPanicEmpty);
    TItem* pItem = (TItem*)getTop();
    return *pItem;
}

TDesC* RCallStack::DumpL()const
{
    HBufC* buffer  = HBufC::NewLC(2*Size());
    TUint8* element = getTop();
    TUint count = Count();
    for(TUint i = 0; i < count; ++i)
    {
        const TItem* elementItem = (const TItem*)element;
        EItemType elementType = elementItem->Type();
        TUint size = elementItem->Size();
        switch(elementType)
        {
        case TStringItemType:{
            const TStringItem* stringItem = static_cast<const TStringItem*>(elementItem);
            TDesC* content = stringItem->toString();
//            buffer->Des().Append(_L("String:"));
            buffer->Des().Append(*content);
            buffer->Des().Append(_L("\r\n"));
            delete content;
        } break;
        default:{
            StackPanic(KPanicDumpDefault);
        }
        }
        element += AlignSize(size);
    }
    
    HBufC* result = HBufC::NewL(buffer->Length());
    result->Des().Append(*buffer);
    CleanupStack::PopAndDestroy(buffer);
    return result;
}







